/*
 * Copyright 2003 Program of Computer Graphics, Cornell University
 *     580 Rhodes Hall
 *     Cornell University
 *     Ithaca NY 14853
 * Web: http://www.graphics.cornell.edu/
 * 
 * Not for commercial use. Do not redistribute without permission.
 */

#ifndef _H_RAY
#define _H_RAY

#include "SIMDSSE.h"
#include <float.h>
#include <memory.h>
#include <math.h>


class ALIGN16 Ray {
public:
  //we store parametric values tStart and tEnd in otherwise unused fourth components of origin and direction
  F32vec4 origin;
  F32vec4 direction;
  float tStart, tEnd;

  float originX() const { return origin[0]; }
  float &originX() { return origin[0]; }
  float originY() const { return origin[1]; }
  float &originY() { return origin[1]; }
  float originZ() const { return origin[2]; }
  float &originZ() { return origin[2]; }
  float dirX() const { return direction[0]; }
  float &dirX() { return direction[0]; }
  float dirY() const { return direction[1]; }
  float &dirY() { return direction[1]; }
  float dirZ() const { return direction[2]; }
  float &dirZ() { return direction[2]; }
  void setOrigin(float x, float y, float z) { origin = F32vec4(1.0f,z,y,x); }
  void setDirection(float x, float y, float z) { direction = F32vec4(1.0f,z,y,x); }
  void setTParameters(float start, float end) { tStart = start; tEnd = end; }
  void setAsSegment(float startX, float startY, float startZ, float endX, float endY, float endZ) { 
    origin = F32vec4(1.0f,startZ,startY,startX);
    direction = F32vec4(1.0f,endZ-startZ,endY-startY,endX-startX);
    tStart = 0; tEnd = 1.0f;
  }
  void setAsSegment(const F32vec4 &start, const F32vec4 &end) {
    origin = start;
    direction = end-start;
    tStart = 0; tEnd = 1.0f;
  }
  F32vec4 getStartPoint() {
    return origin + F32vec4(tStart)*direction;
  }
  F32vec4 getEndPoint() {
    return origin + F32vec4(tEnd)*direction;
  }
  F32vec4 getTValuePoint(float tValue) {
    return origin + F32vec4(tValue)*direction;
  }
};

//RayExt is a ray with a few extra cached fields for faster processing
//we also modify directions slightly to ensure that no component is zero (avoid corner cases)
#define RAY_DIR_MINUS 0
#define RAY_DIR_PLUS  1
class ALIGN16 RayExt : public Ray {
public:
  union { char _dirType[4]; int _dirType_i32; };
  F32vec4 invDirection; 
  F32vec4 v_originX, v_originY, v_originZ;
  F32vec4 v_dirX, v_dirY, v_dirZ;

  float invDirX() const { return invDirection[0]; }
  float &invDirX() { return invDirection[0]; }
  float invDirY() const { return invDirection[1]; }
  float &invDirY() { return invDirection[1]; }
  float invDirZ() const { return invDirection[2]; }
  float &invDirZ() { return invDirection[2]; }
  char dirType(int index) const { assert((index&0x03)==index); return _dirType[index]; }
  char &dirType(int index) { assert((index&0x03)==index); return _dirType[index]; }

  void initializeFromRay(const Ray &ray);
  float originComponent(int index)
  { return origin[index]; }
  float invDirComponent(int index)
  { return invDirection[index]; }
};

//four rays packed into a single structure for SIMD accelerated traversal
class ALIGN16 QuadRay {
public:
  F32vec4 quadOrigin[3]; //x,y,z vectors of origin for each of the four rays
  F32vec4 quadInvDir[3]; //x,y,z vectors of 1/direction for each of the four rays
  F32vec4 quadTStart, quadTEnd;

  RayExt ray[4];

  void initFromRays(int numRays);
};

//function to respond to (or handle) a ray-primitive intersection
//we allow custom functions to potentially allow other types of ray queries such 
//as find any intersection (not just first) or find all intersections 
//An IntersectionResponder may require a special subclass of IntersectionRecord and 
//may alter the ray's tEnd parameter
typedef void (*IntersectionResponder)(class Ray *ray, class IntersectionRecord *, float t, float u, float v, float w, int id);

class IntersectionRecord {
public:
  IntersectionResponder func; //null for default (find first) intersection handling
  int primitiveId;
  float t;
  float u,v,w;

  IntersectionRecord() { func = NULL; primitiveId = -1; }
};

#endif //_H_RAY
